﻿using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Onetube.Entities.CommentsQuery;
using Onetube.Providers.Interfaces;
using Ontube.Entities;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Onetube.Providers
{
    public class YoutubeCommentsProvider : IYoutubeCommentsProvider
    {
        public YoutubeCommentsProvider()
        {
            channelProvider = new YoutubeChannelProvider();
        }

        private Providers.YoutubeChannelProvider channelProvider = null;

        private async Task<CommentsQueryResult> ResolveRequest(StringBuilder petitionUri, TaskCompletionSource<CommentsQueryResult> taskCompletitionSource, CancellationTokenSource cancelTask)
        {
            if (cancelTask == null)
            {
                cancelTask = new CancellationTokenSource();
            }
            HttpWebRequest request = HttpWebRequest.CreateHttp(petitionUri.ToString());
           
            using (HttpClient client = new HttpClient())
            {
                HttpResponseMessage response = await client.GetAsync(petitionUri.ToString(), cancelTask.Token).ConfigureAwait(false);
                
                string resultJson;
                using (StreamReader reader = new StreamReader(await response.Content.ReadAsStreamAsync().ConfigureAwait(false), Encoding.UTF8))
                {
                    try
                    {
                        resultJson = await reader.ReadToEndAsync().ConfigureAwait(false);

                        if (!string.IsNullOrEmpty(resultJson))
                        {
                            var entries = JsonConvert.DeserializeObject<CommentsQueryResult>(resultJson);

                            taskCompletitionSource.SetResult(entries);
                            resultJson = null;
                            entries = null;
                        }
                        else
                        {
                            taskCompletitionSource.SetResult(null);
                        }
                    }
                    catch (Exception ex)
                    {
                        taskCompletitionSource.SetException(ex);
                    }
                }
            }
            return await taskCompletitionSource.Task;
        }

        public async Task<Models.CommentQuery.CommentQueryResult> GetAllCommentsByVideoIdAsync(string videoId, CancellationTokenSource cancelTask, string maxResults = "25", string startIndex = "1")
        {
            //http://dev.gdata.youtube.com/feeds/api/videos/FpRMuUNCVCw/comments?prettyprint=true&alt=json
            StringBuilder petitionUri = new StringBuilder("http://dev.gdata.youtube.com/feeds/api/videos/");

            petitionUri.AppendFormat("{0}", videoId);
            petitionUri.AppendFormat("/comments?{0}", "prettyprint=true");
            petitionUri.AppendFormat("&max-results={0}", maxResults);
            petitionUri.AppendFormat("&start-index={0}", startIndex);
            petitionUri.Append("&alt=json");

            var taskCompletitionSource = new TaskCompletionSource<CommentsQueryResult>();
            Models.CommentQuery.CommentQueryResult CommentResult = new Models.CommentQuery.CommentQueryResult();
            CommentResult.Items = new List<Models.CommentQuery.Item>();
            var result = await ResolveRequest(petitionUri, taskCompletitionSource, cancelTask).ConfigureAwait(false);
            if (result != null)
            {
                var channelProvider = new YoutubeChannelProvider();
                string channelIds = string.Empty;
                int count = 0;
                for (int i = 0; i <= result.Feed.Entry.Count() - 1; i++)
                {
                    count++;
                    var item = result.Feed.Entry.ElementAt(i);
                    if (item.Id != null)
                    {
                        if (count >= result.Feed.Entry.Count())
                        {
                            channelIds += item.YtChannelId.ChannelId;
                        }
                        else
                        {
                            channelIds += item.YtChannelId.ChannelId + ",";
                        }
                    }
                }

                var channelDataList = await channelProvider.SearchChannelByIdAsync(channelIds, cancelTask).ConfigureAwait(false);
                foreach (var item in result.Feed.Entry)
                {
                    var data = new Models.CommentQuery.Item();
                    data.ChannelId = item.YtChannelId.ChannelId;
                    data.Id = item.Id.T;
                    data.PublishedAt = item.Published.published;
                    data.TextContent = item.Content.content;
                    data.Title = item.Title.title;
                    data.ChannelDetails = channelDataList.Items.Where(x => x.Id.Equals(data.ChannelId)).FirstOrDefault();
                    CommentResult.Items.Add(data);
                }
                channelDataList = null;
                channelProvider = null;
                result = null;
            }
          
            return CommentResult;
        }

        public async Task<List<Comment>> GetAllCommentsByVideoAsync(string videoId, string startIndex, CancellationTokenSource token)
        {
            StringBuilder petitionUri = new StringBuilder("https://gdata.youtube.com/feeds/api/");

            petitionUri.AppendFormat("videos/{0}", videoId);
            petitionUri.Append("/comments?");
            petitionUri.AppendFormat("start-index={0}", startIndex);
          
            petitionUri.AppendFormat("&max-results={0}", 10);
           
            petitionUri.Append("&alt=json");
            petitionUri.AppendFormat("&key={0}", AppKeys.API_KEY);

            HttpClient client = new HttpClient();
            string result = string.Empty;
            
            var res = await client.GetAsync(petitionUri.ToString(), token.Token).ConfigureAwait(false);
               
            using (StreamReader reader = new StreamReader(await res.Content.ReadAsStreamAsync().ConfigureAwait(false), Encoding.UTF8))
            {
                try
                {
                    result = await reader.ReadToEndAsync().ConfigureAwait(false);
                }
                catch (Exception ex)
                {
                }
            }
            
            if (string.IsNullOrEmpty(result))
            {
                return null;
            }
            else
            {
            }
            List<Comment> comments = null;
            try
            {
                comments = await GetCommentsAsync(result,token);
            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex.Message);
            }
            client.Dispose();
            res.Dispose();
            result = null;
            if (comments != null && comments.Any())
            {
                result = null;
                return comments;
            }
           
            return null;
        }

        private async Task<List<Comment>> GetCommentsAsync(string data,CancellationTokenSource token)
        {
            /// Posible error al obtener el id del usuario que comenta  "__NO_YOUTUBE_ACCOUNT__"
            CancellationTokenSource cancelTask = token;
            JObject jobject = null;
            List<Comment> commentList = new List<Comment>();
            try
            {
                jobject = JObject.Parse(data);
            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex.Message);
            }
            int availableRecordCount = 0;

            JToken jtoken1 = jobject["feed"];
            if (jtoken1 != null)
            {
                availableRecordCount = ExtentionMethods.JsonValueToInt(jtoken1[(object)"openSearch$totalResults"][(object)"$t"]);
                if (availableRecordCount > 0)
                {
                    JArray jarray = jtoken1[(object)"entry"] as JArray;
                    if (jarray != null)
                    {
                        this.spamCount = 0;
                        foreach (JToken jtoken2 in jarray)
                        {
                            try
                            {
                                if (jtoken2[(object)"yt$spam"] != null)
                                {
                                    ++this.spamCount;
                                }
                                else
                                {
                                    //channelId
                                    Comment comment = new Comment();
                                    comment.Id = ExtentionMethods.JsonValueToString(jtoken2[(object)"id"][(object)"$t"]);
                                    comment.Title = ExtentionMethods.JsonValueToString(jtoken2[(object)"title"][(object)"$t"]);
                                    comment.Content = ExtentionMethods.JsonValueToString(jtoken2[(object)"content"][(object)"$t"]);
                                    comment.Published = ExtentionMethods.JsonValueToDateTime(jtoken2[(object)"published"][(object)"$t"]);
                                    comment.VideoId = ExtentionMethods.JsonValueToString(jtoken2[(object)"yt$videoid"][(object)"$t"]);
                                    comment.ChannelId = ExtentionMethods.JsonValueToString(jtoken2[(object)"yt$channelId"][(object)"$t"]);
                                    comment.IsReplyComment = Enumerable.FirstOrDefault<JToken>(Enumerable.Where<JToken>((IEnumerable<JToken>)jtoken2[(object)"link"], (Func<JToken, bool>)(n => ExtentionMethods.JsonValueToString(n[(object)"rel"]).EndsWith("#in-reply-to")))) != null;
                                    JToken jtoken3 = Enumerable.FirstOrDefault<JToken>((IEnumerable<JToken>)jtoken2[(object)"author"]);
                                    if (jtoken3 != null)
                                    {
                                        comment.Author = new UserInformation();
                                        comment.Author.AuthorName = ExtentionMethods.JsonValueToString(jtoken3[(object)"name"][(object)"$t"]);
                                        comment.Author.AuthorUrl = ExtentionMethods.JsonValueToString(jtoken3[(object)"uri"][(object)"$t"]);
                                        if (!string.IsNullOrEmpty(comment.Author.AuthorUrl))
                                        {
                                            var str = comment.Author.AuthorUrl.Split('/');
                                            comment.Author.AuthorUserId = str.LastOrDefault();
                                            try
                                            {
                                                var channel = await channelProvider.SearchChannelByIdAsync(comment.ChannelId, cancelTask).ConfigureAwait(false);
                                                if (channel != null && channel.Items != null)
                                                {
                                                    var item = channel.Items.FirstOrDefault();
                                                    comment.Channel = item;
                                                    channel = null;
                                                }
                                            }
                                            catch (Exception ex)
                                            {
                                                Debug.WriteLine(string.Format("Channel not found:{0}", ex.Message));
                                            }
                                        }
                                    }
                                    if (!string.IsNullOrEmpty(comment.Content))
                                        commentList.Add(comment);
                                }
                            }
                            catch (Exception ex)
                            {
                                Debug.WriteLine(ex.Message);
                            }
                        }
                    }
                }
            }
            data = null;
            jobject = null;
            return commentList;
        }

        public int spamCount { get; set; }

        public async Task<HttpStatusCode> AddCommentAsync(string comment, string videoId, string accessToken)
        {
            if (!string.IsNullOrEmpty(comment) && !string.IsNullOrEmpty(videoId))
            {
                HttpClient client = new HttpClient();
                var requestUri = string.Format("{0}videos/{1}/comments?alt=json", (object)"https://gdata.youtube.com/feeds/api/", (object)videoId);
                var request = new HttpRequestMessage(HttpMethod.Post, requestUri);
              
                var contentData = GetAddCommentXml(comment);
                request.Content = new StreamContent(new MemoryStream(Encoding.UTF8.GetBytes(contentData)));
                request.Content.Headers.Add("Content-Type", "application/atom+Xml");
                request.Headers.Add("Authorization", "Bearer " + accessToken);
                request.Headers.Add("Host", "gdata.youtube.com");             
                request.Headers.Add("GData-Version", "2.1");
                request.Headers.Add("X-GData-Key", "key=" + AppKeys.API_KEY);

                HttpResponseMessage response = await client.SendAsync(request).ConfigureAwait(false);
                var responseContent = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
                client.Dispose();
                var statusCode = response.StatusCode;
                response.Dispose();
                responseContent = null;
                return statusCode;
            }
            return HttpStatusCode.Forbidden;
        }

        private string GetAddCommentXml(string comment)
        {
            return string.Format("<entry xmlns:yt=\"http://gdata.youtube.com/schemas/2007\" xmlns=\"http://www.w3.org/2005/Atom\"><content>{0}</content></entry>", (object)comment);
        }
    }
}